
#include <iostream>

#include "LuaTinker.h"
#include <ALittleClient/LogSystem/LogSystem.h>

namespace ALittle
{
namespace LuaTinker
{

/*---------------------------------------------------------------------------*/ 
/* __s64                                                                     */ 
/*---------------------------------------------------------------------------*/ 
static int tostring_s64(lua_State *L)
{
	char temp[64];
#ifdef WIN32
	sprintf_s(temp, 64, "%lld", *(long long*)lua_topointer(L, 1));
#else
	sprintf(temp, "%lld", *(long long*)lua_topointer(L, 1));
#endif
	lua_pushstring(L, temp);
	return 1;
}

/*---------------------------------------------------------------------------*/ 
static int eq_s64(lua_State *L)
{
	lua_pushboolean(L, memcmp(lua_topointer(L, 1), lua_topointer(L, 2), sizeof(long long)) == 0);
	return 1;
}

/*---------------------------------------------------------------------------*/ 
static int lt_s64(lua_State *L)
{
	lua_pushboolean(L, memcmp(lua_topointer(L, 1), lua_topointer(L, 2), sizeof(long long)) < 0);
	return 1;
}

/*---------------------------------------------------------------------------*/ 
static int le_s64(lua_State *L)
{
	lua_pushboolean(L, memcmp(lua_topointer(L, 1), lua_topointer(L, 2), sizeof(long long)) <= 0);
	return 1;
}

/*---------------------------------------------------------------------------*/ 
void init_s64(lua_State *L)
{
	const char* name = "__s64";

	lua_newtable(L);

	lua_pushstring(L, "__name");
	lua_pushstring(L, name);
	lua_rawset(L, -3);

	lua_pushstring(L, "__tostring");
	lua_pushcclosure(L, tostring_s64, 0);
	lua_rawset(L, -3);

	lua_pushstring(L, "__eq");
	lua_pushcclosure(L, eq_s64, 0);
	lua_rawset(L, -3);	

	lua_pushstring(L, "__lt");
	lua_pushcclosure(L, lt_s64, 0);
	lua_rawset(L, -3);	

	lua_pushstring(L, "__le");
	lua_pushcclosure(L, le_s64, 0);
	lua_rawset(L, -3);	

	lua_setglobal(L, name);
}

/*---------------------------------------------------------------------------*/ 
/* __u64                                                                     */ 
/*---------------------------------------------------------------------------*/ 
static int tostring_u64(lua_State *L)
{
	char temp[64];
#ifdef WIN32
	sprintf_s(temp, 64, "%llu", *(unsigned long long*)lua_topointer(L, 1));
#else
	sprintf(temp, "%llu", *(unsigned long long*)lua_topointer(L, 1));
#endif
	lua_pushstring(L, temp);
	return 1;
}

/*---------------------------------------------------------------------------*/ 
static int eq_u64(lua_State *L)
{
	lua_pushboolean(L, memcmp(lua_topointer(L, 1), lua_topointer(L, 2), sizeof(unsigned long long)) == 0);
	return 1;
}

/*---------------------------------------------------------------------------*/ 
static int lt_u64(lua_State *L)
{
	lua_pushboolean(L, memcmp(lua_topointer(L, 1), lua_topointer(L, 2), sizeof(unsigned long long)) < 0);
	return 1;
}

/*---------------------------------------------------------------------------*/ 
static int le_u64(lua_State *L)
{
	lua_pushboolean(L, memcmp(lua_topointer(L, 1), lua_topointer(L, 2), sizeof(unsigned long long)) <= 0);
	return 1;
}

/*---------------------------------------------------------------------------*/ 
void init_u64(lua_State *L)
{
	const char* name = "__u64";

	lua_newtable(L);

	lua_pushstring(L, "__name");
	lua_pushstring(L, name);
	lua_rawset(L, -3);

	lua_pushstring(L, "__tostring");
	lua_pushcclosure(L, tostring_u64, 0);
	lua_rawset(L, -3);

	lua_pushstring(L, "__eq");
	lua_pushcclosure(L, eq_u64, 0);
	lua_rawset(L, -3);	

	lua_pushstring(L, "__lt");
	lua_pushcclosure(L, lt_u64, 0);
	lua_rawset(L, -3);	

	lua_pushstring(L, "__le");
	lua_pushcclosure(L, le_u64, 0);
	lua_rawset(L, -3);	

	lua_setglobal(L, name);
}

/*---------------------------------------------------------------------------*/ 
/* init                                                                      */ 
/*---------------------------------------------------------------------------*/ 
void init(lua_State *L)
{
	init_s64(L);
	init_u64(L);
}

/*---------------------------------------------------------------------------*/ 
/* excution                                                                  */ 
/*---------------------------------------------------------------------------*/ 
void dofile(lua_State *L, const char *filename)
{
	lua_pushcclosure(L, on_error, 0);
	int errfunc = lua_gettop(L);

    if(luaL_loadfile(L, filename) == 0)
		lua_pcall(L, 0, 1, errfunc);
	else
		print_error(L, lua_tostring(L, -1));

	lua_remove(L, errfunc);
	lua_pop(L, 1);
}

/*---------------------------------------------------------------------------*/ 
void dostring(lua_State *L, const char* buff)
{
	dobuffer(L, buff, strlen(buff));
}

/*---------------------------------------------------------------------------*/ 
void dobuffer(lua_State *L, const char* buff, size_t len)
{
	lua_pushcclosure(L, on_error, 0);
	int errfunc = lua_gettop(L);

    if(luaL_loadbuffer(L, buff, len, "dobuffer()") == 0)
		lua_pcall(L, 0, 1, errfunc);
	else
		print_error(L, lua_tostring(L, -1));

	lua_remove(L, errfunc);
	lua_pop(L, 1);
}

/*---------------------------------------------------------------------------*/ 
/* debug helpers                                                             */ 
/*---------------------------------------------------------------------------*/ 

void call_stack(lua_State* L, int n)
{
	lua_Debug ar;
	if(lua_getstack(L, n, &ar) != 1) return;
	
	lua_getinfo(L, "nSlu", &ar);

	if(ar.name)
		ALITTLE_INFO(ar.name << "() : line " << ar.currentline << " [" << ar.source << "]");
	else
		ALITTLE_INFO("unknown : line "<< ar.currentline <<" ["<< ar.source <<"]");

	call_stack(L, n+1);
}

int on_error(lua_State *L)
{
	ALITTLE_INFO(lua_tostring(L, -1));
	lua_pop(L, 1);

	call_stack(L, 0);

	return 0;
}

void print_error(lua_State *L, const char* message)
{
	ALITTLE_INFO(message);
	lua_pop(L, 1);
}

/*---------------------------------------------------------------------------*/ 
/* read                                                                      */ 
/*---------------------------------------------------------------------------*/ 
template<>
char* read<char*>(lua_State *L, int index)
{
	return (char*)lua_tostring(L, index);				
}

template<>
const char* read<const char*>(lua_State *L, int index)
{
	return (const char*)lua_tostring(L, index);		
}

template<>
char read<char>(lua_State *L, int index)
{
	return (char)lua_tonumber(L, index);				
}

template<>
unsigned char read<unsigned char>(lua_State *L, int index)
{
	return (unsigned char)lua_tonumber(L, index);		
}

template<>
short read<short>(lua_State *L, int index)
{
	return (short)lua_tonumber(L, index);				
}

template<>
unsigned short read<unsigned short>(lua_State *L, int index)
{
	return (unsigned short)lua_tonumber(L, index);	
}

template<>
long read<long>(lua_State *L, int index)
{
	return (long)lua_tonumber(L, index);				
}

template<>
unsigned long read<unsigned long>(lua_State *L, int index)
{
	return (unsigned long)lua_tonumber(L, index);		
}

template<>
int read<int>(lua_State *L, int index)
{
	return (int)lua_tonumber(L, index);				
}

template<>
unsigned int read<unsigned int>(lua_State *L, int index)
{
	return (unsigned int)lua_tonumber(L, index);		
}

template<>
float read<float>(lua_State *L, int index)
{
	return (float)lua_tonumber(L, index);				
}

template<>
double read<double>(lua_State *L, int index)
{
	return (double)lua_tonumber(L, index);			
}

template<>
bool read<bool>(lua_State *L, int index)
{
	if(lua_isboolean(L, index))
		return lua_toboolean(L, index) != 0;				
	else
		return lua_tonumber(L, index) != 0;
}

template<>
void read<void>(lua_State *L, int index)
{
	return;											
}

template<>
long long read<long long>(lua_State *L, int index)
{
	if(lua_isnumber(L,index))
		return (long long)lua_tonumber(L, index);
	else
		return *(long long*)lua_touserdata(L, index);
}
template<>
unsigned long long read<unsigned long long>(lua_State *L, int index)
{
	if(lua_isnumber(L,index))
		return (unsigned long long)lua_tonumber(L, index);
	else
		return *(unsigned long long*)lua_touserdata(L, index);
}

/*---------------------------------------------------------------------------*/ 
/* push                                                                      */ 
/*---------------------------------------------------------------------------*/ 
template<>
void push(lua_State *L, char ret)
{
	lua_pushnumber(L, ret);						
}

template<>
void push(lua_State *L, unsigned char ret)
{
	lua_pushnumber(L, ret);						
}

template<>
void push(lua_State *L, short ret)
{
	lua_pushnumber(L, ret);						
}

template<>
void push(lua_State *L, unsigned short ret)
{
	lua_pushnumber(L, ret);						
}

template<>
void push(lua_State *L, long ret)
{
	lua_pushnumber(L, ret);						
}

template<>
void push(lua_State *L, unsigned long ret)
{
	lua_pushnumber(L, ret);						
}

template<>
void push(lua_State *L, int ret)
{
	lua_pushnumber(L, ret);						
}

template<>
void push(lua_State *L, unsigned int ret)
{
	lua_pushnumber(L, ret);						
}

template<>
void push(lua_State *L, float ret)
{
	lua_pushnumber(L, ret);						
}

template<>
void push(lua_State *L, double ret)
{
	lua_pushnumber(L, ret);						
}

template<>
void push(lua_State *L, char* ret)
{
	lua_pushstring(L, ret);						
}

template<>
void push(lua_State *L, const char* ret)
{
	lua_pushstring(L, ret);						
}

template<>
void push(lua_State *L, bool ret)
{
	lua_pushboolean(L, ret);						
}

template<>
void push(lua_State *L, long long ret)			
{ 
	*(long long*)lua_newuserdata(L, sizeof(long long)) = ret;
	lua_getglobal(L, "__s64");
	lua_setmetatable(L, -2);
}
template<>
void push(lua_State *L, unsigned long long ret)
{
	*(unsigned long long*)lua_newuserdata(L, sizeof(unsigned long long)) = ret;
	lua_getglobal(L, "__u64");
	lua_setmetatable(L, -2);
}

/*---------------------------------------------------------------------------*/ 
/* pop                                                                       */ 
/*---------------------------------------------------------------------------*/ 
template<>
void pop<void>(lua_State *L)
{
	lua_pop(L, 1);
}

/*---------------------------------------------------------------------------*/ 
/* Tinker Class Helper                                                       */ 
/*---------------------------------------------------------------------------*/ 
static void invoke_parent(lua_State *L)
{

	lua_pushstring(L, "__parent");

	lua_rawget(L, -2);

	

	if(!lua_istable(L,-1)) return
	
	lua_pushvalue(L,2);

	lua_rawget(L, -2);

	if(!lua_isnil(L,-1))
	{
		lua_remove(L,-2);

	}
	else
	{
		lua_remove(L, -1);

		invoke_parent(L);


		lua_remove(L,-2);


	}
}

/*---------------------------------------------------------------------------*/ 
int meta_get(lua_State *L)
{

	lua_getmetatable(L,1);

	lua_pushvalue(L,2);

	lua_rawget(L,-2);

	if(lua_isuserdata(L,-1))
	{

		user2type<var_base*>::invoke(L,-1)->get(L);
		lua_remove(L, -2);
	}
	else if(lua_isnil(L,-1))
	{

		lua_remove(L,-1);

		invoke_parent(L);


		if(lua_isnil(L,-1))
		{
			lua_pushfstring(L, "can't find '%s' class variable. (forgot registering class variable ?)", lua_tostring(L, 2));
			lua_error(L);
		}
	} 



	lua_remove(L,-2);

	return 1;
}

/*---------------------------------------------------------------------------*/ 
int meta_set(lua_State *L)
{

	lua_getmetatable(L,1);

	lua_pushvalue(L,2);

	lua_rawget(L,-2);


	if(lua_isuserdata(L,-1))
	{

		user2type<var_base*>::invoke(L,-1)->set(L);
	}
	else if(lua_isnil(L, -1))
	{
		lua_pushvalue(L,2);

		lua_pushvalue(L,3);

		lua_rawset(L, -4);

	}
	lua_settop(L, 3);

	return 0;
}

} // LuaTinker
} // Sail
